home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Adobe Graphics & Publishing SDK 1996 December
/
Adobe Graphics & Publishing SDK 1996 December.iso
/
mac
/
After Effects 3.1 SDK Mac
/
Examples
/
Effects Samples
/
Gamma (Table)
/
GammaTable.c
< prev
Wrap
C/C++ Source or Header
|
1996-11-06
|
13KB
|
459 lines
/**
GammaTable.c
Part of the Adobe After Effects 3.1 SDK.
Copyright (c)1993-96, Adobe Systems Inc, All Rights Reserved.
This effect does a simple gamma correction of the image pixels.
It allocates space for a lookup table in the sequence data,
and fills that lookup table with the gamma corrected values.
This is a lot faster because:
If the table already contains the correct values, it does
not need to be regenerated for each frame. This means
making a movie where a series of frames all have the
same gamma correction is very quick.
We only have to process 256 values and then can uses that
for each channel of every pixel. This makes it faster
than the GammaNoTable effect even for a single frame.
This demonstrates:
using a sequence data Handle
iterating over the image pixels (w/o using the Iterate callback)
calling the progress callback
a Fixed Slider control
the Extent Hint rectangle (from the InData structure)
making the application display an error alert for us
using the PF_ITERATE callback
Revision History
1.0, created by rb, 12 March 93
1.1, updated for AE 2.0, dmw, 11 Jan 94
1.2, updated for Mac/PowerMac, dmw, 13 Oct 94
2.0, updated for AE 3.0, dmw, 18 Oct 95
2.1, Added call to AEFX_CLR_STRUCT macro to clear out PF_ParamDef, ba, 6 Nov 96
**/
#include "AE_EffectCB.h"
#include "AE_Macros.h"
#include <A4Stuff.h>
#define MAJOR_VERSION 2
#define MINOR_VERSION 0
#define BUG_VERSION 0
#define STAGE_VERSION PF_Stage_RELEASE
#define BUILD_VERSION 0
#define NAME "Gamma (Table)"
/** Parameter Definition Constants
Here we define the parameters, their default
settings, and minimum and maximum values.
**/
enum {
GAMMA_INPUT = 0, /* params index of default input layer */
GAMMA_GAMMA, /* params index of gamma correction factor */
GAMMA_NUM_PARAMS
};
/* We'll allow the gamma correction factor slider vary
* from 0.1 to 5.0, with the default being at 1.0...
* If the user brings up the direct number entry dialog,
* we'll let them type in a value up to 10.0, even though
* the slider values will stop at 5.0...
* All of these numbers are in Fixed point.
*/
#define GAMMA_MIN (6553) /* approximately 0.1 in fixed point */
#define GAMMA_MAX (5L << 16)
#define GAMMA_BIG_MAX (10L << 16)
#define GAMMA_DFLT (1L << 16)
/** Command Specific Subroutines
This plug-in only deals with the commands:
PF_Cmd_ABOUT
PF_Cmd_GLOBAL_SETUP
PF_Cmd_GLOBAL_SETDOWN
PF_Cmd_PARAMS_SETUP
PF_Cmd_SEQUENCE_SETUP
PF_Cmd_SEQUENCE_RESETUP
PF_Cmd_SEQUENCE_SETDOWN
PF_Cmd_RENDER
All other commands are ignored. There is a routine for
each command, and a main routine to dispatch at the bottom.
**/
#define DESCRIPTION "Perform simple image gamma correction."
static PF_Err About (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
PF_SPRINTF(out_data->return_msg, "%s, v%d.%d\r%s",
NAME, MAJOR_VERSION, MINOR_VERSION, DESCRIPTION);
return PF_Err_NONE;
}
static PF_Err GlobalSetup (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
PF_Err err = PF_Err_NONE;
/* Need to let AE know what version of the "Gamma" plug-in
* we are...... */
out_data->my_version = PF_VERSION(MAJOR_VERSION, MINOR_VERSION,
BUG_VERSION, STAGE_VERSION, BUILD_VERSION);
/* We are only going to iterate over the visible pixels,
* i.e. the extent_hint rect, so we need to specify the
* flag for the output extent (you can use the input extent
* rect without telling AE.)
*
* Each result pixel in the output buffer depends only on the value of
* the corresponding input pixel, so I'll also set the PIX_INDEPENDENT OutFlag,
* which can speed up the rendering of field-rendered animations tremendously.
* Be careful when setting this flag, though; if you set it when it's not true,
* field rendered output can be quite wrong.
*/
out_data->out_flags |=
PF_OutFlag_PIX_INDEPENDENT |
PF_OutFlag_USE_OUTPUT_EXTENT;
return err;
}
static PF_Err GlobalSetdown (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
/* We haven't actually allocated any global data,
* so we don't need to do anything here...
*/
return PF_Err_NONE;
}
static PF_Err ParamsSetup (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
PF_Err err = PF_Err_NONE;
PF_ParamDef def; /* scratch space for a parameter definition */
/* Always clear out the PF_ParamDef structure before adding your parameters,
* this macro will do that.
*/
AEFX_CLR_STRUCT(def);
/* Create the FIXED SLIDER parameter... */
def.param_type = PF_Param_FIX_SLIDER;
PF_STRCPY(def.name, "Gamma");
/* NOTE: we must set these strings to empty strings to prevent
* garbage text from being displayed above our sliders... */
def.u.fd.value_str[0] = def.u.fd.value_desc[0] = '\0';
def.u.fd.value = def.u.fd.dephault = GAMMA_DFLT;
/* The min value of the slider and the min value the user
* can type in will both be the same value... */
def.u.fd.valid_min = def.u.fd.slider_min = GAMMA_MIN;
/* The max value of the slider will be smaller than the
* max value the user can type in. This way, the slider
* has a nice feel to it, but the user who really wants
* strange results can access larger values... */
def.u.fd.slider_max = GAMMA_MAX;
def.u.fd.valid_max = GAMMA_BIG_MAX;
/* Let's let the user see 1 decimal place of precision. */
def.u.fd.precision = 1;
def.u.fd.display_flags = 0;
if (err = PF_ADD_PARAM(in_data, -1, &def)) return err;
/* Set number of parameters before leaving */
out_data->num_params = GAMMA_NUM_PARAMS;
return err;
}
/* Gamma Lookup Table
*
* We store a gamma lookup table in the sequence data so that
* each sequence, if rendered in a make movie, can keep its
* own table and can reuse it from frame to frame. The sequence
* data stores the lookup table and the gamma that is stored
* in that lookup table. */
typedef struct {
Fixed gamma_val; /* what is the gamma currently in the table */
unsigned char lut[256]; /* the lookup table */
} GammaTable;
static PF_Err SequenceSetup (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
GammaTable *g_table;
long i;
out_data->sequence_data = NewHandle(sizeof(GammaTable));
if (!out_data->sequence_data) return (PF_Err)memFullErr;
/* generate base table */
HLock(out_data->sequence_data);
g_table = *(GammaTable **)out_data->sequence_data;
g_table->gamma_val = (1L << 16);
for (i=0; i<=255; i++) g_table->lut[i] = (i << 16);
HUnlock(out_data->sequence_data);
/* remember to tell us the size of your sequence data */
out_data->flat_sdata_size = sizeof(GammaTable);
return PF_Err_NONE;
}
static PF_Err SequenceSetdown (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
if (in_data->sequence_data) {
DisposHandle(in_data->sequence_data);
out_data->sequence_data = NULL;
}
return PF_Err_NONE;
}
static PF_Err SequenceResetup (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
/* Just to be paranoid, if somehow we get a Resetup and the
* table is not allocated, then let's try again to allocate it. */
if (!in_data->sequence_data) {
return SequenceSetup(in_data, out_data, params, output);
}
return PF_Err_NONE;
}
/** This is a structure we'll pass to the function we use with the PF_ITERATE
** callback; it's just a pointer to the gamma lookup table. **/
typedef struct {
unsigned char *lut;
} GammaInfo;
/** Here's our iterator func. It just computes the gamma-corrected pixel
** given the lookup table.
**/
static PF_Err GammaFunc (long refcon, long x, long y, PF_Pixel *in, PF_Pixel *out)
{
PF_Err err = PF_Err_NONE;
GammaInfo *gi;
gi = (GammaInfo *)refcon;
out->alpha = in->alpha;
out->red = gi->lut[ in->red ];
out->green = gi->lut[ in->green ];
out->blue = gi->lut[ in->blue ];
return err;
}
/* Render
* To render our image, we will iterate over all the pixels,
* leaving the alpha untouched, and looking up the RGB values
* in the gamma correction lookup table. We will generate
* that table, if we need to, at the start of this routine.
* You will note that 68881 code generation is OFF. */
static PF_Err Render (
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
PF_Err err = PF_Err_NONE;
register long x;
long progress_height;
GammaTable *g_table;
GammaInfo gamma_info;
/* If the gamma factor is exactly 1.0, just copy and return */
if (params[GAMMA_GAMMA]->u.fd.value == (1L << 16)) {
err = PF_COPY(¶ms[0]->u.ld, output, NULL, NULL);
} else {
/* If there is no allocated table, let's bring up an error message.
* In a release version of this effect, since the table is so small,
* it would make more sense to create it on the stack and continue,
* even if the Handle was not allocated. But here we'll show how
* to report an error to the user... */
if (!out_data->sequence_data) {
PF_STRCPY(out_data->return_msg, "Gamma effect invoked without lookup table");
out_data->out_flags |= PF_OutFlag_DISPLAY_ERROR_MESSAGE;
return PF_Err_INTERNAL_STRUCT_DAMAGED;
}
HLock(out_data->sequence_data);
g_table = *(GammaTable **)out_data->sequence_data;
if (g_table->gamma_val != params[GAMMA_GAMMA]->u.fd.value) {
/* if the table doesn't contain the right gamma values,
* we need to regenerate the table contents... */
double temp, gamma;
g_table->gamma_val = params[GAMMA_GAMMA]->u.fd.value;
gamma = (double)g_table->gamma_val / (double)(1L << 16);
gamma = 1.0/gamma;
for (x=0; x<=255; x++) {
temp = PF_POW((double)x / 255.0, gamma);
g_table->lut[x] = (unsigned char)(temp * 255.0);
}
}
/* See the comment in GammaNoTable.c about why we must fill the
* output extent with alpha zero if it does not equal the extent
* over which we are going to iterate. */
if (in_data->extent_hint.left != output->extent_hint.left ||
in_data->extent_hint.top != output->extent_hint.top ||
in_data->extent_hint.right != output->extent_hint.right ||
in_data->extent_hint.bottom != output->extent_hint.bottom) {
err = PF_FILL(NULL, &output->extent_hint, output);
}
if (!err) {
/* Instead of setting up the for loops and incrementing pointers,
* let's use the PF_ITERATE callback. The PF_ITERATE callback
* is pretty smart; it unrolls loops so there's not too much overhead
* in using it, and it can greatly simplify your code -- especially
* if you're just marching over a rectangle. It will automatically call
* the PF_PROGRESS for you, and in the future it might even parallelize
* things for you.
*
* Here we're just iterating over the in_data->extent_hint. We send in the
* height of the extent hint rectangle as the number of steps the progess
* bar will take, and I send in a pointer to my gamma_info structure and the
* address of the actual gamma correction routune. */
progress_height = in_data->extent_hint.top - in_data->extent_hint.bottom;
gamma_info.lut = g_table->lut;
err = PF_ITERATE(0, progress_height,
¶ms[GAMMA_INPUT]->u.ld, &in_data->extent_hint,
(long)&gamma_info, GammaFunc, output);
}
HUnlock(out_data->sequence_data);
}
return err;
}
PF_Err main (
PF_Cmd cmd,
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output )
{
PF_Err err = PF_Err_NONE;
EnterCodeResource(); /* MW specific */
switch (cmd) {
case PF_Cmd_ABOUT:
err = About(in_data,out_data,params,output);
break;
case PF_Cmd_GLOBAL_SETUP:
err = GlobalSetup(in_data,out_data,params,output);
break;
case PF_Cmd_PARAMS_SETUP:
err = ParamsSetup(in_data,out_data,params,output);
break;
case PF_Cmd_GLOBAL_SETDOWN:
err = GlobalSetdown(in_data,out_data,params,output);
break;
case PF_Cmd_SEQUENCE_SETUP:
err = SequenceSetup(in_data,out_data,params,output);
break;
case PF_Cmd_SEQUENCE_SETDOWN:
err = SequenceSetdown(in_data,out_data,params,output);
break;
case PF_Cmd_SEQUENCE_RESETUP:
err = SequenceResetup(in_data,out_data,params,output);
break;
case PF_Cmd_RENDER:
err = Render(in_data,out_data,params,output);
break;
}
ExitCodeResource(); /* MW specific */
return err;
}